package wait_notify.guarded_suspension;

import lombok.extern.slf4j.Slf4j;
import util.Sleeper;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
@Slf4j(topic = "c.GuardedSuspensionPlus")
public class GuardedSuspensionPlus {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new People().start();
        }
        Sleeper.sleep(1);
        for (Integer id : MailBoxes.getIds()) {
            new Postman(id,"content--"+id).start();
        }
    }
}
@Slf4j(topic = "c.People")
class People extends Thread{
    @Override
    public void run() {
        //收信
        GuardObjectPlus guardObject = MailBoxes.guardObjectPlus();
        log.debug("开始收信, id:{}",guardObject.getId());
        Object mail = guardObject.get(5000);
        log.debug("收到信, id:{},内容:{}",guardObject.getId(),mail);
    }
}
@Slf4j(topic = "c.Postman")
class Postman extends Thread{
    private int id;
    private String mail;

    public Postman(int id, String mail) {
        this.id = id;
        this.mail = mail;
    }

    @Override
    public void run() {
        GuardObjectPlus guardObject = MailBoxes.guardObjectPlus(id);
        log.debug("开始送信，id:{}，内容:{}",guardObject.getId(),mail);
        guardObject.complete(mail);
    }
}
class MailBoxes{
    private static final Map<Integer,GuardObjectPlus> boxes = new Hashtable<>();
    private static int id = 1;
    //产生唯一id
    private static synchronized int generateId(){
        return id++;
    }
    public static GuardObjectPlus guardObjectPlus(int id){
        return boxes.remove(id);
    }

    //产生GuardObject
    public static GuardObjectPlus guardObjectPlus(){
        GuardObjectPlus go = new GuardObjectPlus(generateId());
        boxes.put(go.getId(),go);
        return go;
    }

    public static Set<Integer> getIds(){
        return boxes.keySet();
    }
}
class GuardObjectPlus {
    //表示每个GuardObject
    private final int id;
    private Object response;

    GuardObjectPlus(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public Object get() {
        return get(Long.MAX_VALUE);
    }



    public Object get(long timeout) {
        synchronized (this) {
            long begin = System.currentTimeMillis();
            long passedTime = 0;
            while (response == null) {
                long waitTime = timeout-passedTime;
                if (waitTime<=0){
                    break;
                }
                try {
                    this.wait(waitTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                passedTime =  System.currentTimeMillis() - begin;
            }
            return response;
        }
    }

    public void complete(Object response){
        synchronized (this){
            this.response = response;
            this.notifyAll();
        }
    }

}
